<script setup lang="ts">
/**
 * @desc NodeBehavior
 * @author MiyueFE <https://github.com/miyuesc>
 * @since 2024/7/12 上午10:06
 */

import type { ComponentInstance, PropType, Ref } from 'vue'
import { computed, inject, ref, shallowRef } from 'vue'
import TippyPopover from './TippyPopover.vue'
import type { AppendMenuItem, BaseNode, BaseNodeType } from '@/types'
import { getDragData } from '@/utils/element-utils'
import { getGlobalConfig } from '@/configuration/global-config'
import { appendMenuGetter } from '@/injection'

defineOptions({ name: 'NodeBehavior' })

const $props = defineProps({
  data: {
    type: Object as PropType<BaseNode>,
    required: true,
  },
})

const doEmit = defineEmits<{
  append: [type: AppendMenuItem<BaseNodeType>]
  dropped: [node: Ref<BaseNode>]
}>()

const triggerRef = shallowRef<HTMLDivElement>()
const popRef = shallowRef<ComponentInstance<typeof TippyPopover>>()
const droppable = ref(false)
const dropin = ref(false)

const appendMenuGetterFc = inject(appendMenuGetter)

const appendMenus = computed(() => {
  if (!appendMenuGetterFc) {
    return []
  }
  return appendMenuGetterFc?.($props.data) || []
})

function emitClick(item: AppendMenuItem<BaseNodeType>) {
  doEmit('append', item)
  popRef.value?.hidden()
}

async function emitDropNode(ev: DragEvent) {
  ev.preventDefault()
  dropin.value = false
  const node = getDragData()
  if (node && await validateDrop()) {
    doEmit('dropped', node)
  }
}

async function validateDrop() {
  if (!getDragData()) {
    return false
  }
  const canDropped = getGlobalConfig('canDropped')

  if (typeof canDropped === 'function') {
    return canDropped($props.data, getDragData()?.value) as boolean
  }

  return canDropped
}

async function toggleDroppableState(droppin: boolean) {
  dropin.value = droppin
  droppable.value = await validateDrop()
}
</script>

<template>
  <div class="flow-node__behavior">
    <div
      ref="triggerRef"
      class="flow-node__behavior-btn arco-btn arco-btn-primary arco-btn-shape-circle arco-btn-size-medium"
      :class="{ 'flow-node__droppable': dropin && droppable, 'flow-node__not-droppable': dropin && !droppable }"
      @drop.stop="emitDropNode"
      @dragover.capture.stop.prevent="toggleDroppableState(true)"
      @dragleave.capture.stop.prevent="toggleDroppableState(false)"
      @click.stop
      @dblclick.stop
    >
      <svg
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 24 24"
        fill="none"
        stroke="currentColor"
        stroke-width="2"
        stroke-linecap="round"
        stroke-linejoin="round"
        class="icon"
      >
        <path d="M12 5l0 14" />
        <path d="M5 12l14 0" />
      </svg>
    </div>
    <TippyPopover ref="popRef" placement="bottom-start" :target="triggerRef">
      <template #default>
        <div>
          <span class="node-behavior__header">添加节点</span>
          <div class="node-behavior__btn-grid">
            <button v-for="(i, idx) in appendMenus" :key="idx" class="node-behavior__btn" @click.stop="emitClick(i)">
              <a-button v-if="i.icon" type="primary" shape="circle">
                <component :is="i.icon" :size="16" :style="i.iconStyle" />
              </a-button>
              <span class="node-behavior__btn-text">{{ i.name }}</span>
            </button>
          </div>
        </div>
      </template>
    </TippyPopover>
  </div>
</template>
